package entidades;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import enums.EventoAtributos;
import enums.FinalidadeTipoSala;
import enums.SalaAtributos;

/**
 * Classe para gerenciar objetos da classe Sala. Ela possui um atributo que a
 * definir�.
 * 
 * @author Tales, Gabriel, Arthur e Raildo
 * 
 */

public class Sala implements Serializable, Alocavel {

	/**
	 * 
	 */
	private static final long serialVersionUID = -3130558373285301959L;
	private String id;
	private int capacidade;
	private FinalidadeTipoSala finalidadeTipo;
	private String apelido;
	private boolean aberto;
	private List<Escalonavel> eventos;
	private int INDICE_FINALIDADE = 0, INDICE_TIPO = 1;
	private int indiceIterator;
	private final String STRING_VAZIA = "",
			PONTO = ".",
			TRACO = " - ",
			DE_CAPACIDADE = " de capacidade: ",
			TRUE_TO_STRING = "true",
			MSG_EXCEP_TIPO_INVALIDO = "Tipo invalido.",
			MSG_EXCEP_IDENTIFICACAO_INVALIDA = "Identificacao Invalida",
			MSG_EXCEP_EH_FIM_DE_SEMANA = "As salas nao sao alocadas nos fins de semana.",
			MSG_EXCEP_SALA_NAO_DISPONIVEL = "A sala nao esta disponivel neste horario.",
			MSG_EXCEP_SALA_EXCLUSIVA_PARA = "Sala exclusiva para a area de ",
			MSG_EXCEP_ATRIBUTO_INVALIDO = "Atributo inv�lido";

	/**
	 * Construtor de Sala
	 * 
	 * @param id
	 *            - identifica��o da sala
	 * @param capacidade
	 * @param finalidade
	 * @param tipo
	 * @throws Exception
	 */
	public Sala(String id, int capacidade, String finalidade, String tipo)
			throws Exception {

		if (!idDisponivel(id)) {
			throw new Exception(MSG_EXCEP_IDENTIFICACAO_INVALIDA);
		}

		if (finalidade.equalsIgnoreCase(FinalidadeTipoSala.ESCRITORIO.toString())) {
			setApelido(tipo);
		} else {
			setApelido(STRING_VAZIA);
		}

		eventos = new ArrayList<Escalonavel>();
		setId(id);
		setCapacidade(capacidade);
		setFinalidadeTipoSala(finalidade, tipo);
		setAberto(false);

	}

	public Sala(String id, int capacidade, String finalidade, String tipo,
			String apelido) throws Exception {

		if (!idDisponivel(id)) {
			throw new Exception(MSG_EXCEP_IDENTIFICACAO_INVALIDA);
		}

		eventos = new ArrayList<Escalonavel>();
		setId(id);
		setCapacidade(capacidade);
		setFinalidadeTipoSala(finalidade, tipo);
		setApelido(apelido);
		setAberto(false);

	}

	public Sala(String id, int capacidade, String finalidade, String tipo,
			String apelido, boolean aberto) throws Exception {

		if (!idDisponivel(id)) {
			throw new Exception(MSG_EXCEP_IDENTIFICACAO_INVALIDA);
		}

		eventos = new ArrayList<Escalonavel>();
		setId(id);
		setCapacidade(capacidade);
		setFinalidadeTipoSala(finalidade, tipo);
		setApelido(apelido);
		setAberto(aberto);

	}

	/**
	 * Construtor para Encode e Decode
	 */
	public Sala() {

	}
	
	public void setAtributo(String qualAtributo, Object valorDoAtributo){
		if(qualAtributo.equalsIgnoreCase(SalaAtributos.ID.toString())){
			setId((String) valorDoAtributo);
		}
		else if(qualAtributo.equalsIgnoreCase(SalaAtributos.APELIDO.toString())){
			setApelido((String) valorDoAtributo);
		}
		else if(qualAtributo.equalsIgnoreCase(SalaAtributos.CAPACIDADE.toString())){
			setCapacidade((Integer) valorDoAtributo);
		}
		else if(qualAtributo.equalsIgnoreCase(SalaAtributos.FINALIDADE_TIPO.toString())){
			String[] ftValor = (String[]) valorDoAtributo;
			try {
				setFinalidadeTipoSala(ftValor[INDICE_FINALIDADE], ftValor[INDICE_TIPO]);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		else if(qualAtributo.equalsIgnoreCase(SalaAtributos.ABERTO.toString())){
			if(valorDoAtributo.toString().equalsIgnoreCase(TRUE_TO_STRING)){
				setAberto(true);
			}else{
				setAberto(false);
			}
		}
	}
	
	public Object getAtributo(String qualAtributo) throws Exception{
		if(qualAtributo.equalsIgnoreCase(SalaAtributos.ABERTO.toString())){
			return isAberto();
		}
		if(qualAtributo.equalsIgnoreCase(SalaAtributos.APELIDO.toString())){
			return getApelido();
		}
		if(qualAtributo.equalsIgnoreCase(SalaAtributos.CAPACIDADE.toString())){
			return getCapacidade();
		}
		if(qualAtributo.equalsIgnoreCase(SalaAtributos.FINALIDADE.toString())){
			return getFinalidade();
		}
		if(qualAtributo.equalsIgnoreCase(SalaAtributos.TIPO.toString())){
			return getTipo();
		}
		if(qualAtributo.equalsIgnoreCase(SalaAtributos.ID.toString())){
			return getId();
		}
		
		throw new Exception(MSG_EXCEP_ATRIBUTO_INVALIDO);
	}
	
	public List<Escalonavel> getAlocadoPara(){
		return getEventos();
	}
	
	public List<Escalonavel> getEventos() {
		return this.eventos;
	}

	public FinalidadeTipoSala getFinalidadeTipo() {
		return finalidadeTipo;
	}

	public void setFinalidadeTipo(FinalidadeTipoSala finalidadeTipo) {
		this.finalidadeTipo = finalidadeTipo;
	}

	public static long getSerialversionuid() {
		return serialVersionUID;
	}

	public void setId(String id) {
		this.id = id;
	}

	public void setEventos(List<Escalonavel> eventos) {
		this.eventos = eventos;
	}

	/**
	 * verifica se um id dado eh valido
	 * 
	 * @param id
	 * @return um boolean que indica se o id eh valido ou nao
	 */
	private boolean idDisponivel(String id) {
		if (id == null || id.equals(STRING_VAZIA)) {
			return false;
		}
		return true;
	}

	public void alocarEvento(Escalonavel evento) throws Exception {

		if (evento.getAtributo(EventoAtributos.EHFIMDESEMANA.toString()).equals(true)) {
			throw new Exception(MSG_EXCEP_EH_FIM_DE_SEMANA);
		}

		if ((getFinalidade().equalsIgnoreCase(FinalidadeTipoSala.LABORATORIO_COMPUTACAO.getFinalidade()))
				&& (!getTipo().equalsIgnoreCase(FinalidadeTipoSala.LABORATORIO_COMPUTACAO.getTipo()))
				&& !evento.getAtributo(EventoAtributos.AREA.toString()).toString().equalsIgnoreCase(getTipo())) {
			throw new Exception(MSG_EXCEP_SALA_EXCLUSIVA_PARA + getTipo()
					+ PONTO);
		}

		for (Escalonavel ev : eventos) {
			if (conflitoTempo(evento, ev)) {
				throw new Exception(MSG_EXCEP_SALA_NAO_DISPONIVEL);
			}
		}

		eventos.add(evento);
	}

	private boolean conflitoTempo(Escalonavel eventoA, Escalonavel eventoB) throws Exception {
		return ((Tempo) eventoA.getAtributo(EventoAtributos.INTERVALOTEMPO.toString())).conflitoTempo((Tempo)
				eventoB.getAtributo(EventoAtributos.INTERVALOTEMPO.toString()));
	}

	private void desalocarEvento(Escalonavel evento) {
		evento.setAtributo(EventoAtributos.ESTAALOCADO.toString(), false);
		eventos.remove(evento);
	}

	/**
	 * @return the id
	 */
	public String getId() {
		return id;
	}

	/**
	 * @return the capacidade
	 */
	public int getCapacidade() {
		return capacidade;
	}

	/**
	 * @return the apelido
	 */
	public String getApelido() {
		return apelido;
	}

	/**
	 * @return the tipo
	 */
	public String getTipo() {
		return finalidadeTipo.getTipo();
	}

	/**
	 * @return the finalidade
	 */
	public String getFinalidade() {
		return finalidadeTipo.getFinalidade();
	}

	private boolean isAberto() {
		return aberto;
	}

	/**
	 * @param capacidade
	 *            the capacidade to set
	 */
	public void setCapacidade(int capacidade) {
		this.capacidade = capacidade;
	}

	/**
	 * @param apelido
	 *            the apelido to set
	 */
	public void setApelido(String apelido) {
		this.apelido = apelido;
	}

	/**
	 * @param aberto
	 *            the aberto to set
	 */
	public void setAberto(boolean aberto) {
		this.aberto = aberto;
	}

	private void setFinalidadeTipoSala(String finalidade, String tipo)
			throws Exception {

		if (finalidade == null || finalidade == STRING_VAZIA || tipo == null
				|| tipo == STRING_VAZIA) {
			throw new Exception(MSG_EXCEP_TIPO_INVALIDO);
		}

		if (finalidade.equalsIgnoreCase(FinalidadeTipoSala.SALA_AULA_NORMAL.getFinalidade())) {
			if (tipo.equalsIgnoreCase(FinalidadeTipoSala.SALA_AULA_NORMAL.getTipo())) {
				finalidadeTipo = FinalidadeTipoSala.SALA_AULA_NORMAL;
			} else if (tipo.equalsIgnoreCase(FinalidadeTipoSala.SALA_DE_AULA_INTELIGENTE.getTipo())) {
				finalidadeTipo = FinalidadeTipoSala.SALA_DE_AULA_INTELIGENTE;
			} else if (tipo.equalsIgnoreCase(FinalidadeTipoSala.SALA_DE_AULA_VIDEOCONFERENCIA.getTipo())) {
				finalidadeTipo = FinalidadeTipoSala.SALA_DE_AULA_VIDEOCONFERENCIA;
			} else {
				throw new Exception(MSG_EXCEP_TIPO_INVALIDO);
			}
		}

		else if (finalidade.equalsIgnoreCase(FinalidadeTipoSala.LABORATORIO_QUIMICA.getFinalidade())) {
			if (tipo.equalsIgnoreCase(FinalidadeTipoSala.LABORATORIO_QUIMICA.getTipo())) {
				finalidadeTipo = FinalidadeTipoSala.LABORATORIO_QUIMICA;
			} else if (tipo.equalsIgnoreCase(FinalidadeTipoSala.LABORATORIO_FISICA.getTipo())) {
				finalidadeTipo = FinalidadeTipoSala.LABORATORIO_FISICA;
			} else if (tipo.equalsIgnoreCase(FinalidadeTipoSala.LABORATORIO_BIOLOGIA.getTipo())) {
				finalidadeTipo = FinalidadeTipoSala.LABORATORIO_BIOLOGIA;
			} else if (tipo.equalsIgnoreCase(FinalidadeTipoSala.LABORATORIO_COMPUTACAO.getTipo())) {
				finalidadeTipo = FinalidadeTipoSala.LABORATORIO_COMPUTACAO;
			} else {
				throw new Exception(MSG_EXCEP_TIPO_INVALIDO);
			}

		} else if (finalidade.equalsIgnoreCase(FinalidadeTipoSala.ESCRITORIO.getFinalidade())) {
			finalidadeTipo = FinalidadeTipoSala.ESCRITORIO;
		}

		else if (finalidade.equalsIgnoreCase(FinalidadeTipoSala.SALA_DE_CONFERENCIA_NORMAL.getFinalidade())) {
			if (tipo.equalsIgnoreCase(FinalidadeTipoSala.SALA_DE_CONFERENCIA_NORMAL.getTipo())) {
				finalidadeTipo = FinalidadeTipoSala.SALA_DE_CONFERENCIA_NORMAL;
			} else if (tipo.equalsIgnoreCase(FinalidadeTipoSala.SALA_DE_AULA_VIDEOCONFERENCIA.getTipo())) {
				finalidadeTipo = FinalidadeTipoSala.SALA_DE_CONFERENCIA_VIDEOCONFERENCIA;
			} else {
				throw new Exception(MSG_EXCEP_TIPO_INVALIDO);
			}
		}

	}

	public boolean verificaDisponibilidadeHora(Evento evento) throws Exception {
		for (int indice = 0; indice < eventos.size(); indice++) {
			if (conflitoTempo(eventos.get(indice), evento)) {
				throw new Exception(MSG_EXCEP_SALA_NAO_DISPONIVEL);
			}
		}
		return true;
	}

//	public boolean contemEvento(Evento evento) {
//		for (int indice = 0; indice < eventos.size(); indice++) {
//			if (eventos.contains(evento))
//				return true;
//		}
//		return false;
//	}

	@Override
	public String toString() {
		return apelido + TRACO + id + DE_CAPACIDADE + capacidade;
	}
	
	public Iterator<Escalonavel> iterator(){
		indiceIterator = 0;
		return new Iterator<Escalonavel>() {
			
			public boolean hasNext() {
				return indiceIterator < getEventos().size();
			}

			public Escalonavel next() {
				Escalonavel temp = getEventos().get(indiceIterator);
				indiceIterator++;
				return temp;
			}
			
			public void remove() {
				indiceIterator--;
				Escalonavel evento = getEventos().get(indiceIterator);
				desalocarEvento(evento);
			}
			
		};

	}
}